#include "pentominoes.h"
#include <iostream>
#include <time.h>

struct ptData
{
    unsigned int x ;
    unsigned int y ;
} ;

typedef ptData Coord ;


void printPiece(PentominoesPiece* p_pent, Orientation orient)
{
    p_pent->setOrientation(orient) ;
    const MatrixModel& model = p_pent->getMatrixModel() ;

    std::cout << "Printing "<<p_pent->getAlphabet()<<" with Orientation : "<<
                 OrientationStr[model.getOrientation()]<<
                 std::endl ;
    model.printModelWithChar(p_pent->getAlphabet()) ;
    std::cout << std::endl ;
    return ;
}

void testPiece(PentominoesPiece* p_pent)
{
    printPiece(p_pent, R0) ;
    printPiece(p_pent, R90) ;
    printPiece(p_pent, R180) ;
    printPiece(p_pent, R270) ;
    
    printPiece(p_pent, FR0) ;
    printPiece(p_pent, FR90) ;
    printPiece(p_pent, FR180) ;
    printPiece(p_pent, FR270) ;
}

void testMatrixModel()
{
    PentominoesPiece* p_pentL = new PentominoesPiece(ALPHA_L) ;
    PentominoesPiece* p_pentV = new PentominoesPiece(ALPHA_V) ;
    PentominoesPiece* p_pentY = new PentominoesPiece(ALPHA_Y) ;
    PentominoesPiece* p_pentT = new PentominoesPiece(ALPHA_T) ;
    PentominoesPiece* p_pentP = new PentominoesPiece(ALPHA_P) ;
    PentominoesPiece* p_pentU = new PentominoesPiece(ALPHA_U) ;
    PentominoesPiece* p_pentF = new PentominoesPiece(ALPHA_F) ;
    PentominoesPiece* p_pentN = new PentominoesPiece(ALPHA_N) ;
    PentominoesPiece* p_pentZ = new PentominoesPiece(ALPHA_Z) ;
    PentominoesPiece* p_pentI = new PentominoesPiece(ALPHA_I) ;
    PentominoesPiece* p_pentW = new PentominoesPiece(ALPHA_W) ;
    PentominoesPiece* p_pentX = new PentominoesPiece(ALPHA_X) ;

    (void)p_pentL ;
    (void)p_pentV ;
    (void)p_pentY ;
    (void)p_pentT ;

    (void)p_pentP ;
    (void)p_pentU ;
    (void)p_pentF ;
    (void)p_pentN ;

    (void)p_pentZ ;
    (void)p_pentI ;
    (void)p_pentW ;
    (void)p_pentX ;

    testPiece(p_pentL) ;
    testPiece(p_pentV) ;
    testPiece(p_pentY) ;
    testPiece(p_pentT) ;
    testPiece(p_pentP) ;
    testPiece(p_pentU) ;

    testPiece(p_pentF) ;
    testPiece(p_pentN) ;
    testPiece(p_pentZ) ;
    testPiece(p_pentI) ;
    testPiece(p_pentW) ;
    testPiece(p_pentX) ;

    delete p_pentL ;
    delete p_pentV ;
    delete p_pentY ;
    delete p_pentT ;

    delete p_pentP ;
    delete p_pentU ;
    delete p_pentF ;
    delete p_pentN ;

    delete p_pentZ ;
    delete p_pentI ;
    delete p_pentW ;
    delete p_pentX ;    
}

MatrixModel buildGoalRectangle(int numRow, int numCol)
{
    UIntPair goalDimension(numRow, numCol) ;
    MatrixModel goalModel(goalDimension) ;
    for(unsigned int i = 0; i < 6; ++i)
        for(unsigned int j = 0; j < 10; ++j)
            goalModel.setValAt(i, j, true, false) ;

    return goalModel ;
    
}

MatrixModel buildDogModel()
{
    UIntPair goalDimension(11,9) ;
    MatrixModel goalModel(goalDimension) ;
    
    goalModel.setValAt(0,6, true, false) ;
    
    goalModel.setValAt(1,2, true, false) ;
    goalModel.setValAt(1,3, true, false) ;
    goalModel.setValAt(1,4, true, false) ;
    goalModel.setValAt(1,5, true, false) ;
    goalModel.setValAt(1,6, true, false) ;    

    goalModel.setValAt(2,0, true, false) ;    
    goalModel.setValAt(2,1, true, false) ;    
    goalModel.setValAt(2,2, true, false) ;
    goalModel.setValAt(2,3, true, false) ;
    goalModel.setValAt(2,4, true, false) ;
    goalModel.setValAt(2,5, true, false) ;
    goalModel.setValAt(2,6, true, false) ;

    goalModel.setValAt(3,0, true, false) ;    
    goalModel.setValAt(3,1, true, false) ;    
    goalModel.setValAt(3,2, true, false) ;
    goalModel.setValAt(3,3, true, false) ;
    goalModel.setValAt(3,4, true, false) ;
    goalModel.setValAt(3,5, true, false) ;
    goalModel.setValAt(3,6, true, false) ;

    goalModel.setValAt(4,0, true, false) ;    
    goalModel.setValAt(4,1, true, false) ;    
    goalModel.setValAt(4,2, true, false) ;
    goalModel.setValAt(4,3, true, false) ;
    goalModel.setValAt(4,4, true, false) ;
    goalModel.setValAt(4,5, true, false) ;    

    goalModel.setValAt(5,2, true, false) ;
    goalModel.setValAt(5,3, true, false) ;
    goalModel.setValAt(5,4, true, false) ;
    goalModel.setValAt(5,5, true, false) ;

    goalModel.setValAt(6,2, true, false) ;
    goalModel.setValAt(6,3, true, false) ;
    goalModel.setValAt(6,4, true, false) ;
    goalModel.setValAt(6,5, true, false) ;
    goalModel.setValAt(6,6, true, false) ;

    goalModel.setValAt(7,2, true, false) ;
    goalModel.setValAt(7,3, true, false) ;
    goalModel.setValAt(7,4, true, false) ;
    goalModel.setValAt(7,5, true, false) ;
    goalModel.setValAt(7,6, true, false) ;

    goalModel.setValAt(8,2, true, false) ;
    goalModel.setValAt(8,3, true, false) ;
    goalModel.setValAt(8,4, true, false) ;
    goalModel.setValAt(8,5, true, false) ;
    goalModel.setValAt(8,6, true, false) ;
    goalModel.setValAt(8,8, true, false) ;

    goalModel.setValAt(9,2, true, false) ;
    goalModel.setValAt(9,3, true, false) ;
    goalModel.setValAt(9,4, true, false) ;
    goalModel.setValAt(9,5, true, false) ;
    goalModel.setValAt(9,6, true, false) ;
    goalModel.setValAt(9,7, true, false) ;    
    goalModel.setValAt(9,8, true, false) ;

    goalModel.setValAt(10,1, true, false) ;    
    goalModel.setValAt(10,2, true, false) ;
    goalModel.setValAt(10,3, true, false) ;
    goalModel.setValAt(10,4, true, false) ;
    goalModel.setValAt(10,5, true, false) ;
    goalModel.setValAt(10,6, true, false) ;
    goalModel.setValAt(10,7, true, false) ;     
    
    return goalModel ;
}

MatrixModel buildDogWithEyeModel()
{
    UIntPair goalDimension(9,14) ;
    MatrixModel goalModel(goalDimension) ;

    Coord points[60] = {
     {0,1},{0,2},{0,3},
     {1,0},{1,1},{1,3},{1,12},{1,13},
     {2,1},{2,2},{2,3},{2,4},{2,5},{2,6},{2,7},{2,8},{2,9},{2,10},{2,11},{2,12},{2,13},
     {3,3},{3,4},{3,5},{3,6},{3,7},{3,8},{3,9},{3,10},{3,11},{3,12},{3,13},
     {4,3},{4,4},{4,5},{4,6},{4,7},{4,8},{4,9},{4,10},
     {5,3},{5,4},{5,5},{5,6},{5,7},{5,8},{5,9},{5,10},
     {6,4},{6,5},{6,9},{6,10},
     {7,4},{7,5},{7,9},{7,10},
     {8,4},{8,5},{8,9},{8,10},
                       } ;
    for(int i = 0; i < 60; ++i)
        goalModel.setValAt(points[i].x, points[i].y, true, false) ;

    return goalModel ;
}

MatrixModel buildDummyModel()
{
    UIntPair goalDimension(4,9) ;
    MatrixModel goalModel(goalDimension) ;

    goalModel.setValAt(0,0, true, false) ;
    goalModel.setValAt(1,0, true, false) ;
    goalModel.setValAt(2,0, true, false) ;
    goalModel.setValAt(3,0, true, false) ;
    goalModel.setValAt(3,1, true, false) ;

    goalModel.setValAt(0,1, true, false) ;
    goalModel.setValAt(1,1, true, false) ;
    goalModel.setValAt(2,1, true, false) ;
    goalModel.setValAt(2,2, true, false) ;
    goalModel.setValAt(2,3, true, false) ;

    goalModel.setValAt(0,2, true, false) ;
    goalModel.setValAt(0,3, true, false) ;
    goalModel.setValAt(0,4, true, false) ;
    goalModel.setValAt(0,5, true, false) ;
    goalModel.setValAt(1,3, true, false) ;

    goalModel.setValAt(0,6, true, false) ;
    goalModel.setValAt(0,7, true, false) ;
    goalModel.setValAt(0,8, true, false) ;
    goalModel.setValAt(1,7, true, false) ;
    goalModel.setValAt(2,7, true, false) ;

    return goalModel ;
}

int main()
{
    PentominoesTree ptree ;

//     testMatrixModel() ;
    
    MatrixModel goalModel ;
    //goalModel = buildGoalRectangle(6, 10) ;
    //goalModel = buildDummyModel() ;
    //goalModel = buildDogModel() ;
    goalModel = buildDogWithEyeModel() ;

    UIntPair goalDim = goalModel.getDimension() ;
    PentominoesModel pentGoalModel(goalDim, true) ;
    pentGoalModel.setGoalMatrixModel(goalModel) ;

    std::cout << "Printing Goal Model : \n" ;
    goalModel.printModelWithChar('1') ;
    std::cout << std::endl ;

    ptree.setGoalModel(&pentGoalModel) ;
    Solution states ;
    time_t start, end ;
    time(&start) ;
    if (ptree.getASolution(states)) 
        std::cout << "A valid solution found for the problem...\n" ;
    else
        std::cout << "No valid solution found for the problem...\n" ;

    time(&end) ;
    double dif = difftime(end, start) ;
    std::cout << "Time taken to Reach Goal : "<<dif<<std::endl ;
    ptree.printSolution(states) ;
    
    return 0 ;
}
